package org.springframework.security.core.userdetails.jdbc;

import org.springframework.context.ApplicationContextException;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Dillon
 * @date 2024/7/1
 * @slogan 致敬大师 致敬未来的你
 * @desc UserDetailsService 实现，提供从数据库查询用户的方法
 */
public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService, MessageSourceAware {

	/**
	 * 创建用户表，权限表的资源路径
	 */
	public static final String DEFAULT_USER_SCHEMA_DDL_LOCATION = "org/springframework/security/core/userdetails/jdbc/users.ddl";

	/**
	 * 查询数据库用户SQL
	 */
	public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled from users where username = ?";

	/**
	 * 查询用户权限SQL
	 */
	public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "select username,authority from authorities where username = ?";

	/**
	 * 查询用户组SQL
	 */
	public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = "select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id";

	/**
	 * 消息发布类
	 */
	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

	/**
	 * 权限SQL
	 */
	private String authoritiesByUsernameQuery;

	/**
	 * 用户组SQL
	 */
	private String groupAuthoritiesByUsernameQuery;

	/**
	 * 用户SQL
	 */
	private String usersByUsernameQuery;

	private String rolePrefix = "";

	/**
	 * 是否用用户名作为唯一主键
	 */
	private boolean usernameBasedPrimaryKey = true;

	/**
	 * 是否允许权限列表
	 */
	private boolean enableAuthorities = true;

	/**
	 * 是否允许用户组权限列表
	 */
	private boolean enableGroups;

	/**
	 * 构造函数
	 * 初始化默认查询SQL
	 */
	public JdbcDaoImpl() {
		this.usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
		this.authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
		this.groupAuthoritiesByUsernameQuery = DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY;
	}

	/**
	 * 获取消息资源对象
	 *
	 * @return 消息资源对象
	 */
	protected MessageSourceAccessor getMessages() {
		return this.messages;
	}

	/**
	 * 新增用户权限 子类可以复写新增
	 *
	 * @param username    用户名
	 * @param authorities 权限列表
	 */
	protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) {
	}

	/**
	 * 获取查询用户SQL
	 *
	 * @return 查询用户SQL
	 */
	public String getUsersByUsernameQuery() {
		return this.usersByUsernameQuery;
	}

	@Override
	protected void initDao() throws ApplicationContextException {
		Assert.isTrue(this.enableAuthorities || this.enableGroups,
				"Use of either authorities or groups must be enabled");
	}

	/**
	 * 通过用户名从数据库加载userDetails对象
	 *
	 * @param username 用户名
	 * @return 自定义用户都西昂
	 * @throws UsernameNotFoundException 用户未找到异常
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 从数据库获取用户
		List<UserDetails> users = loadUsersByUsername(username);
		// 未查到用户判断
		if (users.isEmpty()) {
			this.logger.debug("通过用户名 '" + username + "' 未查到用户");
			throw new UsernameNotFoundException(this.messages.getMessage("JdbcDaoImpl.notFound", new Object[]{username}, "Username {0} not found"));
		}
		// 获取当前用户
		UserDetails user = users.get(0);
		// 权限、用户组权限收集
		Set<GrantedAuthority> dbAuthsSet = new HashSet<>();
		if (this.enableAuthorities) {
			dbAuthsSet.addAll(loadUserAuthorities(user.getUsername()));
		}
		if (this.enableGroups) {
			dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));
		}
		List<GrantedAuthority> dbAuths = new ArrayList<>(dbAuthsSet);
		// 自定义权限收集
		addCustomAuthorities(user.getUsername(), dbAuths);
		if (dbAuths.isEmpty()) {
			this.logger.debug("用户 '" + username + "' 无权限配置'");
			throw new UsernameNotFoundException(this.messages.getMessage("JdbcDaoImpl.noAuthority", new Object[]{username}, "User {0} has no GrantedAuthority"));
		}
		// 创建自定义用户
		return createUserDetails(username, user, dbAuths);
	}

	/**
	 * 从数据库中获取自定义User对象
	 *
	 * @param username 用户名
	 * @return 自定义用户对象
	 */
	protected List<UserDetails> loadUsersByUsername(String username) {
		// sql查询条件拼接
		RowMapper<UserDetails> mapper = (rs, rowNum) -> {
			String username1 = rs.getString(1);
			String password = rs.getString(2);
			boolean enabled = rs.getBoolean(3);
			return new User(username1, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES);
		};
		// 执行查询
		assert getJdbcTemplate() != null;
		return getJdbcTemplate().query(this.usersByUsernameQuery, mapper, username);
	}

	/**
	 * 从数据库中获取权限列表
	 *
	 * @param username 用户名
	 * @return 权限列表
	 */
	protected List<GrantedAuthority> loadUserAuthorities(String username) {
		assert getJdbcTemplate() != null;
		return getJdbcTemplate().query(this.authoritiesByUsernameQuery, new String[]{username}, (rs, rowNum) -> {
			String roleName = JdbcDaoImpl.this.rolePrefix + rs.getString(2);
			return new SimpleGrantedAuthority(roleName);
		});
	}

	/**
	 * 从数据库中获取用户组列表
	 *
	 * @param username 用户名
	 * @return 用户组列表
	 */
	protected List<GrantedAuthority> loadGroupAuthorities(String username) {
		assert getJdbcTemplate() != null;
		return getJdbcTemplate().query(this.groupAuthoritiesByUsernameQuery, new String[]{username},
				(rs, rowNum) -> {
					String roleName = getRolePrefix() + rs.getString(3);
					return new SimpleGrantedAuthority(roleName);
				});
	}

	/**
	 * 创建自定义用户
	 *
	 * @param username            用户名
	 * @param userFromUserQuery   用户查询sql
	 * @param combinedAuthorities 权限列表
	 * @return 自定义用户对象
	 */
	protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery,
			List<GrantedAuthority> combinedAuthorities) {
		// 获取用户名
		String returnUsername = userFromUserQuery.getUsername();
		if (!this.usernameBasedPrimaryKey) {
			returnUsername = username;
		}
		// 初始化自定义用户对象
		return new User(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(),
				userFromUserQuery.isAccountNonExpired(), userFromUserQuery.isCredentialsNonExpired(),
				userFromUserQuery.isAccountNonLocked(), combinedAuthorities);
	}

	/**
	 * 设置权限查询SQL
	 *
	 * @param queryString 权限查询SQL
	 */
	public void setAuthoritiesByUsernameQuery(String queryString) {
		this.authoritiesByUsernameQuery = queryString;
	}

	/**
	 * 获取权限查询SQL
	 *
	 * @return 权限查询SQL
	 */
	protected String getAuthoritiesByUsernameQuery() {
		return this.authoritiesByUsernameQuery;
	}

	/**
	 * 设置用户组查询SQL
	 *
	 * @param queryString 用户组查询SQL
	 */
	public void setGroupAuthoritiesByUsernameQuery(String queryString) {
		this.groupAuthoritiesByUsernameQuery = queryString;
	}

	/**
	 * 设置角色前缀
	 *
	 * @param rolePrefix 角色前缀
	 */
	public void setRolePrefix(String rolePrefix) {
		this.rolePrefix = rolePrefix;
	}

	/**
	 * 获取角色前缀 默认  空
	 *
	 * @return 角色前缀
	 */
	protected String getRolePrefix() {
		return this.rolePrefix;
	}

	/**
	 * 设置是否使用用户名作为主键
	 *
	 * @param usernameBasedPrimaryKey 配置值
	 */
	public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
		this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
	}

	/**
	 * 获取是否使用用户名作为主键
	 *
	 * @return 配置值
	 */
	protected boolean isUsernameBasedPrimaryKey() {
		return this.usernameBasedPrimaryKey;
	}

	/**
	 * 设置查询用户SQL
	 *
	 * @param usersByUsernameQueryString 查询用户SQL
	 */
	public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
		this.usersByUsernameQuery = usersByUsernameQueryString;
	}

	/**
	 * 获取是否允许资源权限配置
	 *
	 * @return 设置值
	 */
	protected boolean getEnableAuthorities() {
		return this.enableAuthorities;
	}

	/**
	 * 设置是否允许资源权限 默认允许
	 *
	 * @param enableAuthorities 设置值
	 */
	public void setEnableAuthorities(boolean enableAuthorities) {
		this.enableAuthorities = enableAuthorities;
	}

	/**
	 * 获取是否允许组权限配置
	 *
	 * @return 配置结果
	 */
	protected boolean getEnableGroups() {
		return this.enableGroups;
	}

	/**
	 * 设置是否允许组权限
	 * 默认不允许
	 *
	 * @param enableGroups 是否允许组权限
	 */
	public void setEnableGroups(boolean enableGroups) {
		this.enableGroups = enableGroups;
	}

	/**
	 * 设置自定义消息对象
	 *
	 * @param messageSource 消息对象
	 */
	@Override
	public void setMessageSource(MessageSource messageSource) {
		Assert.notNull(messageSource, "消息资源不能为空");
		this.messages = new MessageSourceAccessor(messageSource);
	}

}
